/*
 * $Id: ChannelRefresh.java 112 2008-02-14 00:34:50Z jasta00 $
 *
 * Copyright (C) 2007 Josh Guilfoyle <jasta@devtcg.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * TODO: This class needs to be generalized much better, with specialized
 * parsers for Atom 1.0, Atom 0.3, RSS 0.91, RSS 1.0 and RSS 2.0.  Hell,
 * this whole thing needs to be chucked and redone.
 */

package Android.TestXMLHTTP.parser;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import Android.TestXMLHTTP.provider.RSSReader;
import Android.TestXMLHTTP.util.DateUtils;
import android.content.ContentResolver;
import android.content.ContentValues;


public class ChannelRefresh extends DefaultHandler
{

	/* Buffer post information as we learn it in STATE_IN_ITEM. */
	private ChannelPost mPostBuf;
	
	private static ArrayList<HashMap<String, String>> result;

	/* Efficiency is the name of the game here... */
	private int mState;
	private static final int STATE_IN_ITEM = (1 << 2);
	private static final int STATE_IN_ITEM_TITLE = (1 << 3);
	private static final int STATE_IN_ITEM_LINK = (1 << 4);
	private static final int STATE_IN_ITEM_DESC = (1 << 5);
	private static final int STATE_IN_ITEM_DATE = (1 << 6);
	private static final int STATE_IN_ITEM_AUTHOR = (1 << 7);
	private static final int STATE_IN_TITLE = (1 << 8);

	private static HashMap<String, Integer> mStateMap;

	static
	{
		mStateMap = new HashMap<String, Integer>();
		mStateMap.put("item", new Integer(STATE_IN_ITEM));
		mStateMap.put("entry", new Integer(STATE_IN_ITEM));
		mStateMap.put("title", new Integer(STATE_IN_ITEM_TITLE));
		mStateMap.put("link", new Integer(STATE_IN_ITEM_LINK));
		mStateMap.put("description", new Integer(STATE_IN_ITEM_DESC));
		mStateMap.put("content", new Integer(STATE_IN_ITEM_DESC));
		mStateMap.put("content:encoded", new Integer(STATE_IN_ITEM_DESC));
		mStateMap.put("dc:date", new Integer(STATE_IN_ITEM_DATE));
		mStateMap.put("updated", new Integer(STATE_IN_ITEM_DATE));
		mStateMap.put("pubDate", new Integer(STATE_IN_ITEM_DATE));
		mStateMap.put("dc:author", new Integer(STATE_IN_ITEM_AUTHOR));
		mStateMap.put("author", new Integer(STATE_IN_ITEM_AUTHOR));
	}

	public ChannelRefresh()
	{
		super();
		//mContent = resolver;
		result = new ArrayList<HashMap<String, String>>();
	}

	/*
	 * Note that if syncDB is called with id == -1, this class will interpret
	 * that to mean that a new channel is being added (and also tested) so
	 * the first meaningful piece of data encountered will trigger an insert
	 * into the database.
	 *
	 * This logic is all just terrible, but this entire class needs to be
	 * scrapped and redone to make room for improved cooperation with the rest
	 * of the application.
	 */
	public ArrayList<HashMap<String, String>> parseFeed(String url)
	{
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp;
        try {
             sp = spf.newSAXParser();
             XMLReader xr = sp.getXMLReader();
             xr.setContentHandler(this);
             xr.setErrorHandler(this);
             
             URLConnection c = (new URL(url)).openConnection();
             c.setRequestProperty("User-Agent", "Android/m3-rc37a");
             xr.parse(new InputSource(c.getInputStream()));
             
        } catch (SAXParseException e) {
             e.printStackTrace();
        } catch (ParserConfigurationException e) {
             e.printStackTrace();
        } catch (SAXException e) {
             e.printStackTrace();
        } catch (MalformedURLException e) {
             e.printStackTrace();
        } catch (IOException e) {
             e.printStackTrace();
        }
        
        return result;
	}

	public void startElement(String uri, String name, String qName,
			Attributes attrs)
	{
		Integer state = mStateMap.get(qName);

		if (state != null)
		{
			mState |= state.intValue();

			if (state.intValue() == STATE_IN_ITEM)
				mPostBuf = new ChannelPost();
			else if ((mState & STATE_IN_ITEM) != 0 && state.intValue() == STATE_IN_ITEM_LINK)
			{
				String href = attrs.getValue("href");

				if (href != null)
					mPostBuf.link = href;
			}
		}
	}
	
	public void endElement(String uri, String name, String qName)
	{
		Integer state = mStateMap.get(qName);

		if (state != null)
		{
			mState &= ~(state.intValue());

			if (state.intValue() == STATE_IN_ITEM)
			{
				ContentValues values = new ContentValues();
				
				System.out.println("Test: " + 
						mPostBuf.title + "   " + 
						mPostBuf.link + "   " + 
						mPostBuf.author + "   " + 
						mPostBuf.desc);
				
				values.put(RSSReader.Posts.TITLE, mPostBuf.title);
				values.put(RSSReader.Posts.URL, mPostBuf.link);
				values.put(RSSReader.Posts.AUTHOR, mPostBuf.author);
				values.put(RSSReader.Posts.DATE, mPostBuf.getDate());
				values.put(RSSReader.Posts.BODY, mPostBuf.desc);
				
  				HashMap<String, String> hm = new HashMap<String, String>();
				Set<Entry<String, Object>> vs = values.valueSet();

				Iterator<Entry<String, Object>> it = vs.iterator();
				while (it.hasNext()) {
					Entry<String, Object> e = it.next();
					// System.out.println("Test: " + e.getKey() + "   "
					// + e.getValue());
					hm.put(e.getKey(), e.getValue().toString());
				}
                result.add(hm);
			}
		}
	}

	public void characters(char ch[], int start, int length)
	{
		if ((mState & STATE_IN_ITEM) == 0)
			return;

		/*
		 * We sort of pretended that mState was inclusive, but really only
		 * STATE_IN_ITEM is inclusive here.  This is a goofy design, but it is
		 * done to make this code a bit simpler and more efficient.
		 */
		switch (mState)
		{
		case STATE_IN_ITEM | STATE_IN_ITEM_TITLE:
			mPostBuf.title = new String(ch, start, length);
			break;
		case STATE_IN_ITEM | STATE_IN_ITEM_DESC:
			mPostBuf.desc = new String(ch, start, length);
			break;
		case STATE_IN_ITEM | STATE_IN_ITEM_LINK:
			mPostBuf.link = new String(ch, start, length);
			break;
		case STATE_IN_ITEM | STATE_IN_ITEM_DATE:
			mPostBuf.setDate(new String(ch, start, length));
			break;
		case STATE_IN_ITEM | STATE_IN_ITEM_AUTHOR:
			mPostBuf.author = new String(ch, start, length);
			break;
		default:
			/* Don't care... */
		}
	}

	/* TODO: Create org.devtcg.provider.dao.* classes for this. */
	private class ChannelPost
	{
		public String title;
		public Date date;
		public String desc;
		public String link;
		public String author;

		public ChannelPost()
		{
			/* Empty. */
		}

		public void setDate(String str)
		{
			date = DateUtils.parseDate(str);

			if (date == null)
				date = new Date();
		}

		public String getDate()
		{
			return DateUtils.formatDate(mPostBuf.date);
		}
	}
}
